﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Prism.Properties;

namespace Microsoft.Practices.ServiceLocation
{
	/// <summary>
	/// This class is a helper that provides a default implementation for most of the methods of <see cref="T:Microsoft.Practices.ServiceLocation.IServiceLocator"/>.
	/// </summary>
	public abstract class ServiceLocatorImplBase : IServiceLocator, IServiceProvider
	{
		#region IServiecLocator members

		/// <summary>
		/// Get an instance of the given <paramref name="serviceType"/>.
		/// </summary>
		/// <param name="serviceType">Type of object requested.</param>
		/// <returns>The requested service instance.</returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is an error resolving the service instance.
		/// </exception>
		public virtual object GetInstance(Type serviceType)
		{
			return this.GetInstance(serviceType, (string)null);
		}

		/// <summary>
		/// Get an instance of the given named <paramref name="serviceType"/>.
		/// </summary>
		/// <param name="serviceType">Type of object requested.</param>
		/// <param name="key">Name the object was registered with.</param>
		/// <returns>The requested service instance.</returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is an error resolving the service instance.
		/// </exception>
		public virtual object GetInstance(Type serviceType, string key)
		{
			try
			{
				return this.DoGetInstance(serviceType, key);
			}
			catch (Exception ex)
			{
				throw new ActivationException(this.FormatActivationExceptionMessage(ex, serviceType, key), ex);
			}
		}

		/// <summary>
		/// Get all instances of the given <paramref name="serviceType"/> currently registered in the container.
		/// </summary>
		/// <param name="serviceType">Type of object requested.</param>
		/// <returns>A sequence of instances of the requested <paramref name="serviceType"/>.</returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is are errors resolving the service instance.
		///</exception>
		public virtual IEnumerable<object> GetAllInstances(Type serviceType)
		{
			try
			{
				return this.DoGetAllInstances(serviceType);
			}
			catch (Exception ex)
			{
				throw new ActivationException(this.FormatActivateAllExceptionMessage(ex, serviceType), ex);
			}
		}

		/// <summary>
		/// Get an instance of the given <typeparamref name="TService"/>.
		/// </summary>
		/// <typeparam name="TService">Type of object requested.</typeparam>
		/// <returns>The requested service instance.</returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is are errors resolving the service instance.
		///</exception>
		public virtual TService GetInstance<TService>()
		{
			return (TService)this.GetInstance(typeof(TService), (string)null);
		}

		/// <summary>
		/// Get an instance of the given named <typeparamref name="TService"/>.
		/// </summary>
		/// <typeparam name="TService">Type of object requested.</typeparam>
		/// <param name="key">Name the object was registered with.</param>
		/// <returns>The requested service instance.</returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is are errors resolving the service instance.
		/// </exception>
		public virtual TService GetInstance<TService>(string key)
		{
			return (TService)this.GetInstance(typeof(TService), key);
		}

		/// <summary>
		/// Get all instances of the given <typeparamref name="TService"/> currently registered in the container.
		/// </summary>
		/// <typeparam name="TService">Type of object requested.</typeparam>
		/// <returns>A sequence of instances of the requested <typeparamref name="TService"/>.</returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is are errors resolving the service instance.<
		/// /exception>
		public virtual IEnumerable<TService> GetAllInstances<TService>()
		{
			return this.GetAllInstances(typeof(TService)).OfType<TService>();
		}

		#endregion

		#region IServiceProvider members

		/// <summary>
		/// Implementation of <see cref="M:System.IServiceProvider.GetService(System.Type)"/>.
		/// </summary>
		/// <param name="serviceType">The requested service.</param>
		/// <returns>
		/// The requested object.
		/// </returns>
		/// <exception cref="T:Microsoft.Practices.ServiceLocation.ActivationException">
		/// If there is an error in resolving the service instance.
		/// </exception>
		public virtual object GetService(Type serviceType)
		{
			return this.GetInstance(serviceType, (string)null);
		}

		#endregion

		/// <summary>
		/// When implemented by inheriting classes, this method will do the actual work of resolving
		/// the requested service instance.
		/// </summary>
		/// <param name="serviceType">Type of instance requested.</param>
		/// <param name="key">Name of registered service you want. May be null.</param>
		/// <returns>The requested service instance.</returns>
		protected abstract object DoGetInstance(Type serviceType, string key);

		/// <summary>
		/// When implemented by inheriting classes, this method will do the actual work of
		/// resolving all the requested service instances.
		/// </summary>
		/// <param name="serviceType">Type of service requested.</param>
		/// <returns>Sequence of service instance objects.</returns>
		protected abstract IEnumerable<object> DoGetAllInstances(Type serviceType);

		/// <summary>
		/// Format the exception message for use in an <see cref="T:Microsoft.Practices.ServiceLocation.ActivationException"/>
		/// that occurs while resolving multiple service instances.
		/// </summary>
		/// <param name="actualException">The actual exception thrown by the implementation.</param><param name="serviceType">Type of service requested.</param>
		/// <returns>The formatted exception message string.</returns>
		protected virtual string FormatActivateAllExceptionMessage(Exception actualException, Type serviceType)
		{
			return string.Format(
				CultureInfo.CurrentUICulture,
				Resources.ActivateAllExceptionMessage,
				serviceType.Name);
		}

		/// <summary>
		/// Format the exception message for use in an <see cref="T:Microsoft.Practices.ServiceLocation.ActivationException"/>
		/// that occurs while resolving a single service.
		/// </summary>
		/// <param name="actualException">The actual exception thrown by the implementation.</param>
		/// <param name="serviceType">Type of service requested.</param>
		/// <param name="key">Name requested.</param>
		/// <returns>The formatted exception message string.</returns>
		protected virtual string FormatActivationExceptionMessage(Exception actualException, Type serviceType, string key)
		{
			return string.Format(
				CultureInfo.CurrentUICulture,
				Resources.ActivationExceptionMessage,
				serviceType.Name,
				key);
		}
	}
}